<?php

namespace Modules\Inventory\Repository;

use Illuminate\Support\Facades\DB;
use Modules\Inventory\Models\Inventory;
use App\Enums\Org;
use App\Enums\InvTransType;
use Modules\Inventory\Models\InventorySettlement;

/**
 * 即时库存相关
 * Class InventoryRepository
 * @package Modules\Production\Repository
 */
class InventoryRepository
{
    /**
     * 根据品号查询即时库存信息
     * @param String $materialNo
     * @return mixed
     */
    static public function getInventoryByMaterialNo(String $materialNo,$cargo = '',$orgCode = '')
    {
        return Inventory::where('material_no',$materialNo)->where('cargo_code',$cargo)->where('org_code',$orgCode)->get();
    }

    /**
     * 根据品号查询即时库存信息
     * @param String $materialNo
     * @return mixed
     */
    static public function getInventoryByLotnum(String $lotnum,$cargo = '',$orgCode = '')
    {
        return Inventory::where('lot_num',$lotnum)->where('cargo_code',$cargo)->where('org_code',$orgCode)->get();
    }

    /**
     * 根据ID查询一条库存信息
     * @param $id
     * @return mixed
     */
    static public function getById($id){
        return Inventory::lockForUpdate()->find($id);
    }

    /**
     * 给指定id库存增加分配数量
     * @param $invId
     * @param $qty
     * @return mixed
     */
    static public function incAssignQtyById($invId,$qty){
        return Inventory::where('id',$invId)->lockForUpdate()->increment('assign_qty',$qty);
    }

    /**
     * 给指定id库存释放分配数量
     * @param $invId
     * @param $qty
     * @return mixed
     */
    static public function releaseAssignQtyById($invId,$qty){
        return Inventory::where('id',$invId)->lockForUpdate()->decrement('assign_qty',$qty);
    }

    /**
     * 添加一条库存记录
     * @param $data
     * @return mixed
     */
    static public function addIntentory($data){
        return Inventory::create($data);
    }

    /**
     * 根据库位、箱号查询库存信息
     * @param $materialNo
     * @param $location
     * @param $lotnum
     * @return mixed
     */
    static public function getInvByLocationAndLotnum($orgCode,$cargoCode,$materialNo,$location,$lotnum){
        return Inventory::where([
            ['org_code','=',$orgCode],
            ['cargo_code','=',$cargoCode],
            ['material_no','=',$materialNo],
            ['location','=',$location],
            ['lot_num','=',$lotnum]
        ])->lockForUpdate()->first();
    }

    /**
     * 根据库位、箱号增加库存
     * @param $materialNo
     * @param $location
     * @param $lotnum
     * @param $qty
     */
    static public function updateByLocatonAndLotnum($orgCode,$cargoCode,$materialNo,$location,$lotnum,$qty){
        return Inventory::where([
            ['org_code','=',$orgCode],
            ['cargo_code','=',$cargoCode],
            ['material_no','=',$materialNo],
            ['location','=',$location],
            ['lot_num','=',$lotnum]
        ])->lockForUpdate()->increment('qty',$qty);
    }

    /**
     * 给指定库存id增加库存数量
     * @param $invId
     * @param $qty
     * @return mixed
     */
    static public function incrementQtyById($invId,$qty){
        return Inventory::where('id',$invId)->lockForUpdate()->increment('qty',$qty);
    }

    /**
     * 扣减库存余量和分配库存
     * @param $invId
     * @param $qty
     * @return mixed
     */
    static public function decQtyAndAssignQtyById($invId,$qty){
        return Inventory::where('id',$invId)->lockForUpdate()->update(
            [
                'qty' => DB::raw("qty - {$qty}"),
                'assign_qty' => DB::raw("assign_qty - {$qty}"),
            ]
        );
    }

    /**
     * 按库存id直接扣减库存余量
     * @param $invId
     * @param $qty
     * @return mixed
     */
    static public function decQtyById($invId,$qty){
        return Inventory::where('id',$invId)->lockForUpdate()->decrement('qty',$qty);
    }

    /**
     * 根据库位代码查询库存信息
     * @param $orgCode
     * @param $location
     * @return mixed
     */
    static public function getInventoryByLocationCode($orgCode,$location){
        return Inventory::where('org_code',$orgCode)->where('location',$location)->get();
    }

    /**
     * 检查指定箱号是否存在可用库存
     * @param $lotnum
     * @return bool
     */
    static public function hasQtyByLotnum($lotnum){
        $invByLotnum = Inventory::select('lot_num',DB::raw('sum(qty) as qty'),DB::raw('sum(assign_qty) as assign_qty'),DB::raw('sum(lock_qty) as lock_qty'))
            ->where('lot_num',$lotnum)
            ->groupBy('lot_num')
            ->first();
        if(!$invByLotnum){
            return false;
        }

        return ($invByLotnum->qty - $invByLotnum->assign_qty) > 0 ? true : false;

    }

    /**
     * 即时库存分页列表
     * @param $request
     * @return mixed
     */
    static public function getList($request){

        $builder = self::getWhere($request);

        $limit = isset($request['limit']) ? $request['limit'] : 100;

        $list = $builder->orderBy('id','desc')->with(['material','locations','wms'])->whereHas('material',function($query) use($request){
            if(isset($request['material_name']) && $request['material_name'] != ''){
                $query->where('fname','like',"%{$request['material_name']}%");
            }
        })->paginate($limit)->toArray();

        foreach ($list['data'] as &$val){
            $val['org_name']    = Org::labelBy($val['org_code']);
            $val['carogo_name'] = Org::labelBy($val['cargo_code']);
            $val['qty']         = floatval($val['qty']);
            $val['assign_qty']  = floatval($val['assign_qty']);
            $val['lock_qty']    = floatval($val['lock_qty']);
            $val['release_qty'] = $val['qty'] - $val['assign_qty'] - $val['lock_qty'];
        }

        return $list;

    }

     /**
     * 查询条件拼接
     * @param $request
     * @return Inventory
     */
    static public function getWhere($request){

        $builder = new Inventory();

        $canSearchFields = $builder->getFields();

        foreach ($request as $key => $val){

            if(!in_array($key,$canSearchFields) || $val == ''){
                continue;
            }

            if($key == 'location' && isset($request['location_like']) && $request['location_like'] == 1){
                //库位模糊查询
                $builder = $builder->where($key,'like',"%{$val}%");
            }else{
                $builder = $builder->where($key,$val);
            }
        }

        return $builder;

    }



   
    /*
     * 月结算表分页
     * @param $request
     * @return mixed
     */
    static public function getListStatement($request){

        // \DB::connection()->enableQueryLog(); // 开启
        $builder = self::getWhereStatement($request);

        $limit = isset($request['limit']) ? $request['limit'] : 100;
        
        $list = $builder->orderBy('id','desc')->with(['material','locations','wms'])->paginate($limit)->toArray();
        
        // $sqllog = \DB::getQueryLog(); // 获取日志
        // p($sqllog);
        // exit;
        foreach ($list['data'] as &$val){
            $val['org_name']    = Org::labelBy($val['org_code']);
            $val['carogo_name'] = Org::labelBy($val['cargo_code']);
            $val['qty']         = floatval($val['qty']);
            $val['assign_qty']  = floatval($val['assign_qty']);
            $val['lock_qty']    = floatval($val['lock_qty']);
            $val['release_qty'] = $val['qty'] - $val['assign_qty'] - $val['lock_qty'];
        }

        return $list;

    }
    
    
    


     /**
     * 月结算-查询条件拼接
     * @param $request
     * @return Inventory
     */
    static public function getWhereStatement($request){

        $builder = new InventorySettlement();

        $canSearchFields = $builder->getFields();
        
    
        foreach ($request as $key => $val){
            
            if(!in_array($key,$canSearchFields) || $val == ''){
                continue;
            }
            

            if($key == 'location' && isset($request['location_like']) && $request['location_like'] == 1){
                //库位模糊查询
                $builder = $builder->where($key,'like',"%{$val}%");
                
            }else{
                
                if ($key=='created_at') {
                    $date = strtotime($val);
                    $end = strtotime(date('Y-m-01', $date).' 00:00');
                    $str = strtotime(date('Y-m-t', $date).' 23:59');
                    $builder = $builder->where($key,'>=',$end);
                    $builder = $builder->where($key,'<=',$str);
                }else{
                    $builder = $builder->where($key,$val);
                }

            }
        }

        return $builder;
    }
    
    
    
    /*
     * 月结算表-数据插入
     * @param $request
     * @return mixed
     */
    static public function storeByStatement(){

      
        ini_set ('memory_limit', '128000000M');
        $InventorySettlement = new InventorySettlement();
        $list = Inventory::select($InventorySettlement->getFields())->orderBy('id','desc')->get()->toArray();
     
        //数组分块处理
        $chunck = collect($list);
        $chunks = $chunck->chunk(1000);
        $chunks->all();
        
        try {
            //开始起事务
            DB::beginTransaction();
            
            foreach ($chunks as $k=>$v){
                $InventorySettlement->insert(json_decode($v,true));
            }
            
            DB::commit();
            return true;
        } catch (\Exception $e) {
            DB::rollBack();
           
        }  
            
        
    }
    

   

    /**
     * 重算即时库存
     * @param $orgCode
     * @param $cargoCode
     * @param $materialNo
     * @param $lotnum
     * @param $location
     * @return float
     * @throws \Exception
     */
    static public function resetInventory($orgCode,$cargoCode,$materialNo,$lotnum,$location){

        $qty = 0.0000;

        $transLog = InventoryTransRepository::getBy($orgCode,$cargoCode,$materialNo,$lotnum,$location);
        if($transLog->isEmpty()){
            return $qty;
        }

        //print_r($transLog->toArray());exit();

        foreach ($transLog as $logItem){

            $startQty = $qty;

            if($logItem->trans_type == InvTransType::INBOUND->value){
                //入库
                $qty += $logItem->qty;

            }elseif($logItem->trans_type == InvTransType::OUTBOUND->value){
                //出库
                $qty -= $logItem->qty;
            }else{
                //其他
                if($qty < $logItem->qty){
                    throw new \Exception("库存重算失败，出现负库存【{$logItem->id}】");
                }
            }

            if($qty < 0){
                throw new \Exception("库存重算失败，出现负库存【{$logItem->id}】");
            }

            //更新期初期末
            if($logItem->start_qty != $startQty || $logItem->end_qty != $qty){
                InventoryTransRepository::updateBy($logItem->id,[
                   'start_qty' => $startQty,
                    'end_qty' => $qty
                ]);
            }

        }

        return $qty;

    }

}
